package com.audaque.springboot.foshanupload.web.webdemo.controller;

import com.audaque.springboot.foshanupload.core.util.ReflectUtil;
import com.audaque.springboot.foshanupload.log.anno.WebLogAnno;
import com.audaque.springboot.foshanupload.web.webdemo.model.domain.Employee;
import com.audaque.springboot.foshanupload.web.webdemo.model.domain.OuterClass;
import com.audaque.springboot.foshanupload.web.webdemo.model.domain.Person;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.ReflectionUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.beans.BeanInfo;
import java.beans.Introspector;
import java.lang.annotation.Annotation;
import java.lang.reflect.*;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@Slf4j
@RestController
@RequestMapping("reflect")
public class ReflectController {

    @WebLogAnno
    @GetMapping("/test1")
    public void test1() throws Exception {
        //获取对应的class对象
        Class clazz = Class.forName(Person.class.getCanonicalName());
        Class clazz1 = OuterClass.MemberInstanceInnerClass.class;


        //类的名称
        String name = clazz.getName();
        String canonicalName = clazz.getCanonicalName();
        String simpleName = clazz.getSimpleName();
        String name1 = clazz1.getName();
        String canonicalName1 = clazz1.getCanonicalName();
        String simpleName1 = clazz1.getSimpleName();



        String typeName = clazz.getTypeName();
        TypeVariable[] typeParameters = clazz.getTypeParameters();



        Object o = clazz.newInstance();
        Class clazz2 = o.getClass();

        Method method = clazz.getMethod("test", String.class);
        //方法的名称
        String methodName = method.getName();

        String r1 = (String) method.invoke(o, "参数");
        String r2 = (String) ReflectionUtils.invokeMethod(method, o, new Object[]{"参数"});
        //获取修饰符
        int mod = clazz.getModifiers();


        Package aPackage = clazz.getPackage();
        //包的名称
        String packageName = aPackage.getName();

        Class superclass = clazz.getSuperclass();
        Class<? extends Class> aClass = clazz.getClass();
        Class<?>[] arrClass = clazz.getClasses();
        //通过getDeclaredFields()方法获取对象类中的所有属性（含私有）
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            String fieldName = field.getName();
            Class<? extends Field> aClass1 = field.getClass();
            String name2 = field.getName();
            Class<?> type = field.getType();
            Type genericType = field.getGenericType();
        }

        //  获取目标对象上所有的注解
        Annotation[] arrs = clazz.getAnnotations();
        //判断对象上是否有某种类型的注解
        clazz.isAnnotationPresent(Data.class);
        // 希望提供有关其 bean 的显式信息的 bean 实现者可以提供某个 BeanInfo 类，
        BeanInfo beanInfo = Introspector.getBeanInfo(Person.class, Object.class);
    }

    @WebLogAnno
    @GetMapping("/test2")
    public void test2() throws Exception {

        // 获取外部类的 Class 对象
        Class<?> outerClass = Class.forName("com.audaque.springboot.foshanupload.web.webdemo.model.domain.OuterClass");
        Class<?>[] innerClasses = OuterClass.class.getDeclaredClasses();
        Class<?>[] innerClasses2 = outerClass.getDeclaredClasses();

        // 获取内部类的 Class 对象
        Class<?> memberInstanceInnerClass = Class.forName("com.audaque.springboot.foshanupload.web.webdemo.model.domain.OuterClass$MemberInstanceInnerClass");
        Class<?> memberStaticInnerClass = Class.forName("com.audaque.springboot.foshanupload.web.webdemo.model.domain.OuterClass$MemberStaticInnerClass");


        Constructor<?> outerClassConstructor = outerClass.getDeclaredConstructor();
        Constructor<?> memberInstanceInnerConstructor = memberInstanceInnerClass.getDeclaredConstructor(OuterClass.class);
        Constructor<?> memberStaticInnerConstructor = memberStaticInnerClass.getDeclaredConstructor();


        outerClassConstructor.setAccessible(true);
        memberInstanceInnerConstructor.setAccessible(true);
        memberStaticInnerConstructor.setAccessible(true);


        Object outerObj = outerClassConstructor.newInstance();
        Object memberInstanceInnerObj = memberInstanceInnerConstructor.newInstance(new OuterClass());
        Object memberStaticInnerObj = memberStaticInnerConstructor.newInstance();


        Method outerMethod = outerClass.getDeclaredMethod("outerMethod");
        Method memberInstanceInnerMethod = memberInstanceInnerClass.getDeclaredMethod("innerMethod");
        Method memberStaticInnerMethod = memberStaticInnerClass.getDeclaredMethod("staticInnerMethod");


        outerMethod.setAccessible(true);
        memberInstanceInnerMethod.setAccessible(true);
        memberStaticInnerMethod.setAccessible(true);


        outerMethod.invoke(outerObj);
        memberInstanceInnerMethod.invoke(memberInstanceInnerObj);
        memberStaticInnerMethod.invoke(memberStaticInnerObj);

    }

    @WebLogAnno
    @GetMapping("/test3")
    public List<String> test3() throws Exception {
        Class<?> aClass = Employee.class;
        List<Class> list = new ArrayList<>();
        loopClass(aClass, list);
        List<String> collect = list.stream().map(e -> e.getName()).collect(Collectors.toList());
        return collect;
    }

    @WebLogAnno
    @GetMapping("/test4")
    public List<String> test4() throws Exception {
        List<String> usedClassName = new ArrayList<>();
        Class<?> aClass = null;
        try {
            aClass = Class.forName("java.util.ArrayList");
        } catch (Exception e) {
            throw new Exception("获取字节码对象时异常");
        }
        if (aClass != null) {
            usedClassName = ReflectUtil.getUsedClassName(aClass,true);
        }
        return usedClassName;
    }


    @WebLogAnno
    @GetMapping("/test5")
    public void test5() throws Exception {
        List<String> list = new ArrayList<>();
        Class<?> clazz = list.getClass();
        log.info(clazz.getName());
        log.info(clazz.getTypeName());

    }





    private void loopClass(Class<?> aClass, List<Class> list) {
        List<Class> listAll = new ArrayList<>();
        List<Class> listLoopField = new ArrayList<>();
        List<Class> listLoopInnerClass = new ArrayList<>();
        List<Class> listLoopSupperClass = new ArrayList<>();
        List<Class> listClass = new ArrayList<>();
        loopInnerClass(aClass, listLoopInnerClass);
        loopSupperClass(aClass, listLoopSupperClass);
        listClass.add(aClass);
        listClass.addAll(listLoopInnerClass);
        listClass.addAll(listLoopSupperClass);
        for (Class item : listClass) {
            loopFieldClass(item, listLoopField);
        }
        listAll.addAll(listLoopInnerClass);
        listAll.addAll(listLoopSupperClass);
        listAll.addAll(listLoopField);
        list.add(aClass);
        for (Class item : listAll) {
            if (!list.contains(item)) {
                loopClass(item, list);
            }
        }
    }


    private void loopFieldClass(Class<?> aClass, List<Class> list) {
        Field[] declaredFields = aClass.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            Class<?> type = declaredField.getType();
            list.add(type);
            if (!list.contains(type)) {
                loopFieldClass(type, list);
            }
        }
    }

    private void loopSupperClass(Class<?> aClass, List<Class> list) {
        if (aClass != null) {
            Class<?> superclass = aClass.getSuperclass();
            if (superclass != null) {
                list.add(superclass);
            }
            if (!list.contains(superclass)) {
                loopSupperClass(superclass, list);
            }
        }
    }

    private void loopInnerClass(Class<?> aClass, List<Class> list) {
        if (aClass != null) {
            Class<?>[] declaredClasses = aClass.getDeclaredClasses();
            if (declaredClasses.length > 0) {
                List<Class> declaredClassList = Stream.of(declaredClasses).collect(Collectors.toList());
                if (declaredClasses != null && declaredClassList.size() > 0) {
                    list.addAll(declaredClassList);
                    for (Class aClass1 : declaredClassList) {
                        if (!list.contains(aClass1)) {
                            loopInnerClass(aClass1, list);
                        }
                    }
                }
            }


        }
    }

}
